package com.remoter.context.spring;

import java.util.Set;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScanBeanDefinitionParser;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;

import com.remoter.api.configure.IConfiguration;
import com.remoter.api.extension.support.ExtensionLoader;
import com.remoter.api.util.Final;
import com.remoter.api.util.StringUtil;
import com.remoter.context.spring.annotation.Provider;
import com.remoter.context.spring.bean.ProviderBean;
import com.remoter.context.spring.boot.RemoterContextStarter;

public class RemoterBeanDefinitionParser extends ComponentScanBeanDefinitionParser{
	
	private static final String BASE_PACKAGE = "package";
	private static final String BASE_CONFIG = "config";
	
	@Override
	public BeanDefinition parse(Element element, ParserContext parserContext){
		String basePackage = element.getAttribute(BASE_PACKAGE);
		String baseConfig = element.getAttribute(BASE_CONFIG);
		if(StringUtil.isBlank(basePackage)){
			throw new IllegalArgumentException("package is empty");
		}
		if(StringUtil.isBlank(baseConfig)){
			baseConfig = "remoter.properties";
		}
		if(baseConfig.indexOf(".")<=0){
			throw new IllegalArgumentException("config format error");
		}
		String[] config = baseConfig.split("\\.");
		System.setProperty(Final.K_CONFIGURATION_NAME,config[0]);
		System.setProperty(Final.K_CONFIGURATION_PATH,config[1]);
		ExtensionLoader.getService(IConfiguration.class,baseConfig);
		
		String[] basePackages = StringUtils.tokenizeToStringArray(basePackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
		RemoterClassPathBeanDefinitionScanner scanner = new RemoterClassPathBeanDefinitionScanner(parserContext);
		Set<BeanDefinitionHolder> beanDefinitions = scanner.scanComplete(basePackages);
		
		RootBeanDefinition remoterContext = new RootBeanDefinition(RemoterContextStarter.class);
		remoterContext.getConstructorArgumentValues().addGenericArgumentValue(baseConfig);
		remoterContext.setLazyInit(false);
		remoterContext.setInitMethodName("start");
		remoterContext.setDestroyMethodName("close");
		parserContext.getRegistry().registerBeanDefinition(RemoterContextStarter.ID,remoterContext);
		
		for(BeanDefinitionHolder holder : beanDefinitions){
			try{
				ScannedGenericBeanDefinition srcDefinition = (ScannedGenericBeanDefinition)holder.getBeanDefinition();
				String beanClass = srcDefinition.getBeanClassName();
				Class<?> type = Class.forName(beanClass);
				AnnotationMetadata metadata = srcDefinition.getMetadata();
				
				RootBeanDefinition beanDefinition = new RootBeanDefinition(ProviderBean.class);
				beanDefinition.setLazyInit(false);
				beanDefinition.setBeanClassName(ProviderBean.class.getName());
				
				RootBeanDefinition classDefinition = new RootBeanDefinition(type);
                classDefinition.setLazyInit(false);
                
                Provider provider = type.getAnnotation(Provider.class);
                String[] interfaces = metadata.getInterfaceNames();
                if(null == interfaces || interfaces.length == 0){
                	throw new IllegalStateException("class [ "+beanClass+" ] no interfaces detected");
                }else{
                	Class<?> interfaceTarget = provider.type();
                	if(null == interfaceTarget){
                		throw new IllegalStateException("class [ "+beanClass+" ] cannot find suitable interface");
                	}else{
                		String id = interfaceTarget.getName();
                		int counter = 2;
                		while(parserContext.getRegistry().containsBeanDefinition(id)){
                			id = interfaceTarget.getName() + (counter ++);
                		}
                		if(parserContext.getRegistry().containsBeanDefinition(id)){
                			throw new IllegalStateException("duplicate spring bean id " + id);
                		}
                		beanDefinition.getPropertyValues().addPropertyValue("id",id);
                		beanDefinition.getPropertyValues().addPropertyValue("type",interfaceTarget);
                		beanDefinition.getPropertyValues().addPropertyValue("proxy",new BeanDefinitionHolder(classDefinition,provider.name()));
                		
                		beanDefinition.getPropertyValues().addPropertyValue("weight",provider.weight());
                		beanDefinition.getPropertyValues().addPropertyValue("executes",provider.executes());
                		parserContext.getRegistry().registerBeanDefinition(provider.name(),beanDefinition);
                	}
                }
				
			}catch(Exception e){
				e.printStackTrace();
			}
			
		}
		
		RootBeanDefinition consumerBeanDefinition = new RootBeanDefinition(RemoterAnnotationBeanPostProcessor.class);
		consumerBeanDefinition.setSource(parserContext.getReaderContext().extractSource(element));
		consumerBeanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
		parserContext.getReaderContext().getRegistry().registerBeanDefinition(RemoterAnnotationBeanPostProcessor.class.getName(),consumerBeanDefinition);
		
		return null;
	}
	
}